{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Parameter guide" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What is a parameter?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A parameter usually represents a single value of a single feature of an instrument, \n", "e.g. the frequency of a function generator, the mode of a multimeter (resistance, current, or voltage), \n", "or the input impedance of an oscilloscope channel. Basic attributes of a `Parameter` are:\n", "\n", " * `name`: the name used internally by QCoDeS, e.g. 'input_impedance'\n", " * `label`: the label to use for plotting this parameter\n", " * `unit`: the physical unit. ALWAYS use SI units if a unit is applicable\n", " * `set_cmd`, the command to set the parameter. Either a SCPI string with a single '{}', or a function taking one argument (see examples below)\n", " * `get_cmd`: the command to get the parameter. Follows the same scheme as `set_cmd`\n", " \n", "Note that there are many more attributes, the full list can be found in the Parameter documentation.\n", " \n", "A basic example of a parameter is `microwave_source.frequency`, in which case `microwave_source` is an `Instrument`, and the Parameter is `frequency`. \n", "Getting and setting the microwave frequency can be performed as such:\n", "\n", "```Python\n", "microwave_source.frequency(42e9) # Set microwave source frequency to 42 GHz\n", "microwave_source.frequency() # Get frequency\n", ">>> 42e9\n", "```\n", "\n", "Note that these set/get commands do not simply set a variable in Python; usually they actually send VISA commands to the instrument to get/set the frequency. For examples, look at the QCoDeS drivers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In QCoDes, measurements are performed by varying parameter values and measuring other parameters.\n", "This is one of the primary motivations for creating a Parameter: having a standardized way for setting/getting/saving data during a measurement. \n", "This is both an advantage and disadvantage. \n", "The advantage is that information about the Parameter being measured is known beforehand (e.g. name, unit, shape), and so does not need to be specified when creating a measurement.\n", "The disadvantage is that requiring everything measured to be a Parameter removes some flexibility, as one cannot simply run arbitrary Python code, though there are ways to (partially) circumvent this.\n", "\n", "**Note**: The new QCoDeS DataSet has dropped the requirement of only sweeping and measuring Parameters (more specifically it does not rely on the `Loop` class). However, our group's version of QCoDeS has not switched to this method." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parameter types\n", "\n", "There are three basic types of Parameters, categorized by the type of value it holds:\n", "- **Parameter**\n", " The Parameter is the most common type of Parameter. \n", " It can hold a single value (e.g. int, float, bool, string).\n", "- **ArrayParameter**\n", " The ArrayParameter can hold an array of values. \n", " The array shape should be specified so that size of the `DataArray` can be determined during a measurement.\n", "- **MultiParameter**\n", " The MultiParameter can hold multiple values.\n", " Each of these values can be either a single value (like a `Parameter`), or an array (like an `ArrayParameter`).\n", "\n", "In this notebook, we will focus on the standard `Parameter`. \n", "For details on the `ArrayParameter` and `MultiParameter` see the `docs/examples/Parameters.ipynb` notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Instrument-independent parameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parameters need not be attached to Instruments; they can be created independent of them. \n", "Here we see a simple example, where a Parameter simply holds a value:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "42" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from qcodes import Parameter\n", "param = Parameter('param', set_cmd=None) # Default set_cmd is False, in which case we cannot set it (see below)\n", "\n", "param(42) # set value\n", "param() # get value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parameters have many features, the two most important of which are functions called during `get`/`set`. \n", "The simplest method to do this is via the keyword arguments `get_cmd` and `set_cmd` passed during instantiation:\n", "\n", "- **get_cmd**: Function to call during get command. \n", " Must accept zero arguments, and return value. \n", " If attached to an Instrument, it can also be a string, which corresponds to the VISA command. \n", " Can also be\n", " - **False**: Raise an error when a get is called\n", " - **None**: Return latest set value (This is the default)\n", " \n", "- **set_cmd**: Function to call during set command\n", " Must accept one argument, which is the value being set \n", " If attached to an Instrument, it can also be a string, which corresponds to the VISA command. \n", " Can also be **False** and **None** (see above), the default being **False**.\n", " \n", "As an example, here we create a parameter with a specific action during get and set:\n", "- During a `get`, it will return a random value between 0 and 1\n", "- During a `set`, it will print the value it's being set to." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Value set to 42\n" ] }, { "data": { "text/plain": [ "0.960839463495372" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "def print_value(value):\n", " print('Value set to', value)\n", " \n", "p = Parameter('param', get_cmd=np.random.uniform, set_cmd=print_value)\n", "\n", "p(42) # Setting calls a print statement\n", "p() # Get return a random value between 0 and 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parameters have many more features related to it's get/set functionality, such as validation, ramping, value parsing/mapping, scaling, etc. These are all attributes of parameters and can also be changed after instantiation. \n", "For a full list, look at the documentation of the `Parameter` and `_BaseParameter`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Subclassing parameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While the `get_cmd`/`set_cmd` are sufficient for most needs, more complex situations can arise where simple commands do not suffice. For example, the parameter is not passed as `self`, and so the get/set function cannot access other parameter attributes. In these cases, it is better to subclass the Parameter and add these functionalities in the subclass.\n", "\n", "To demonstrate this, we create a parameter that either returns the latest set value, or the exponent of that value depending on the boolean attribute `exponentiate`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class ExponentiateParameter(Parameter):\n", " exponentiate = False\n", " \n", " def get_raw(self):\n", " if self.exponentiate:\n", " return np.exp(self.raw_value)\n", " else:\n", " return self.raw_value" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Without exponentiation: 3\n", "With exponentiation: 20.085536923187668\n" ] } ], "source": [ "exponentiate_parameter = ExponentiateParameter('exponentiation', initial_value=3)\n", "\n", "print('Without exponentiation:', exponentiate_parameter())\n", "\n", "exponentiate_parameter.exponentiate = True\n", "print('With exponentiation:', exponentiate_parameter())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Subclassing Parameters can be a very useful way to create complex parameters with many functions (e.g. a retuning sequence).\n", "\n", "**Note**: A third way to add get/set commands is through the ParameterNode (see below)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parameter set callbacks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are situations where we want an ancillary function to be called every time a parameter is set. \n", "This is facilitated by connecting a callback to a parameter. \n", "\n", "In this example, we have a parameter to set the magnetic field. \n", "Our goal is to measure the temperature every time the magnetic field is changed. \n", "We do this by attaching the function `measure_temperature` to the magnetic field parameter (via `magnetic_field.connect`)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Temperature is at 53 K\n", "Setting magnetic field to 1T\n", "Temperature is at 241 K\n", "Setting magnetic field to 2T\n", "Temperature is at 54 K\n" ] } ], "source": [ "def measure_temperature(value):\n", " print(f'Temperature is at {np.random.randint(0, 300)} K')\n", "\n", "magnetic_field = Parameter('magnetic_field', set_cmd=None)\n", "magnetic_field.connect(measure_temperature,\n", " update=True) # Update value to perform initial temperature_measurement\n", "\n", "print('Setting magnetic field to 1T')\n", "magnetic_field(1)\n", "\n", "print('Setting magnetic field to 2T')\n", "magnetic_field(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parameters can also be connected to each other. These can also have a scale and offset applied to the value. \n", "Here we connect `param2` to `param1` with an offset of 2:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "44" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "param1 = Parameter('param1', set_cmd=None)\n", "param2 = Parameter('param2', set_cmd=None)\n", "param1.connect(param2, offset=2, update=False)\n", "\n", "param1(42)\n", "param2()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Linking a parameter to config\n", "\n", "**Note**: This feature is only available with SilQ, which uses the `SubConfig`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The config is a dictionary containing general settings related to the experiment. \n", "Parameters can not only be connected to other parameters, they can also be connected to a key in the config. \n", "This has the advantage that every time the config is updated, the parameter will reflect this value." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'properties': {'read_duration': 1}}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from silq import config\n", "config.properties.read_duration = 1\n", "config" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "read_duration = Parameter('read_duration', set_cmd=None, initial_value=0)\n", "read_duration()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now connect the parameter to the config path `properties.read_duration`. \n", "Initially, this won't change the value:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "read_duration.set_config_link('config:properties.read_duration') # Note that config paths start with `config:`\n", "read_duration()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, once we modify the value in the config, the parameter value does change:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "config.properties.read_duration = 2\n", "read_duration()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.3" } }, "nbformat": 4, "nbformat_minor": 2 }